﻿using MachineLearning.BackPropagatingNeuralNetwork;
using MongoDB.Bson;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Utility;

namespace LearningWorkbench
{
    public partial class NeuralNetworkManager : Form
    {
        private Thread _neuralNetworkThread;
        private Thread[] _neuralTrainingThreads;
        private string text1, text2, text3, text4;
        private int _unfinishedAnns;

        public NeuralNetworkManager()
        {
            InitializeComponent();
        }

        public void parallelTrain(object index)
        {
            var annindex = index as int?;
            Console.WriteLine(annindex + " training starts");
            //var tann = WorkbenchModel.Anns[annindex.Value];

            //tann.Train();
            var tann = WorkbenchModel.BAnns[annindex.Value];
            /*
            var subTarget = from target in WorkbenchModel.Targets
                            where WorkbenchModel.ClusterResult.ClusterOf(target) == annindex
                            select target;
            var subInput = from input in WorkbenchModel.Inputs
                     where WorkbenchModel.ClusterResult.ClusterOf(input) == annindex
                     select input;
            */

            var subInput = new List<double[]>();
            var subTarget = new List<double[]>();

            for (int i = 0; i < WorkbenchModel.Inputs.Count; ++i)
            {
                if (getCluster(i) == annindex)
                {
                    subInput.Add(WorkbenchModel.Inputs[i]);
                    subTarget.Add(WorkbenchModel.Targets[i]);
                }
            }
            tann.Train(subInput.ToList(), subTarget.ToList());
            //progressBar1.PerformStep();

            if (--_unfinishedAnns == 0)
                MessageBox.Show("Neural Network Training Complete!");

            Console.WriteLine(annindex + " training completes " + _unfinishedAnns + " are left!");
        }

        public void train()
        {
            var nhiddenlayers = int.Parse(text3);
            var snodes = text4.Split(',');
            var nnodes = new int[nhiddenlayers];
            var learningr = double.Parse(text1);
            var momentum = double.Parse(text2);
            var sizeins = WorkbenchModel.PrepInputFieldNames.Count;
            var sizetrs = WorkbenchModel.PrepTargetFieldNames.Count;
            var anns = WorkbenchModel.ClusterResult.Centroids.Count();

            //WorkbenchModel.Anns = new NeuralNetworkTeacher[anns];
            WorkbenchModel.BAnns = new NeuralNetwork[anns];
            _neuralTrainingThreads = new Thread[anns];

            for (var i = 0; i < nhiddenlayers; ++i)
            {
                nnodes[i] = int.Parse(snodes[i]);
            }

            for (var i = 0; i < anns; ++i)
            {
                //WorkbenchModel.Anns[i] = new NeuralNetworkTeacher(sizeins, sizetrs, nnodes,
                //    momentum, learningr);
                WorkbenchModel.BAnns[i] = new NeuralNetwork(sizeins, nnodes[0], sizetrs)
                {
                    LearningRate = learningr,
                    Momentum = momentum
                };
            }

            /*
            for (int i = 0; i < WorkbenchModel.Inputs.Count; ++i)
            {
                var cluster = getCluster(i);
                WorkbenchModel.Anns[cluster].AddInputTargetPair(WorkbenchModel.Inputs[i], 
                    WorkbenchModel.Targets[i]);
            }
             * */

            _unfinishedAnns = anns;
            

            for (int i = 0; i < anns; ++i)
            {
                Console.WriteLine(i + " thread start");
                _neuralTrainingThreads[i] = new Thread(parallelTrain);
                _neuralTrainingThreads[i].Start(i);
            }
        }

        private int getCluster(int index)
        {
            if (WorkbenchModel.ClusterResult.ClusteredDataSet == null)
            {
                var input = WorkbenchModel.Inputs[index];
                return WorkbenchModel.ClusterResult.ClusterOf(input);
            }

            return WorkbenchModel.ClusterResult.ClusteredDataSet[index].ClusterNumber;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (textBox1.Text != null && textBox3.Text != null
                && textBox2.Text != null && textBox4.Text != null)
            {
                text1 = textBox1.Text;
                text2 = textBox2.Text;
                text3 = textBox3.Text;
                text4 = textBox4.Text;
                _neuralNetworkThread.Start();
            }
        }

        private void NeuralNetworkManager_Load(object sender, EventArgs e)
        {
            _neuralNetworkThread = new Thread(train);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            /*
            foreach (var ann in WorkbenchModel.Anns)
            {
                if (!ann.Complete)
                {
                    MessageBox.Show("Not finished yet!");
                    return;
                }
            }
             * */

            if (_unfinishedAnns == 0)
            {
                DialogResult = System.Windows.Forms.DialogResult.OK;
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            var models = new List<NeuralNetworkModel>();

            foreach (var ann in WorkbenchModel.BAnns)
            {
                models.Add(ann.Model);
            }

            FileManager<NeuralNetworkModel>.Save("neural_network.json", models);
            MessageBox.Show("neural_network.json has been saved");
            DialogResult = System.Windows.Forms.DialogResult.OK;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            var anns = FileManager<NeuralNetworkModel>.Load("neural_network.json");
            WorkbenchModel.BAnns = new NeuralNetwork[anns.Count];

            for (int i = 0; i < anns.Count; ++i)
            {
                WorkbenchModel.BAnns[i] = new NeuralNetwork(anns[i]);
            }

            MessageBox.Show("neural_network.json has been loaded");
            DialogResult = System.Windows.Forms.DialogResult.OK;
        }
    }
}
